package org.gjy.m8.concurrent.lock;

import java.util.concurrent.locks.StampedLock;

/**
 * @author gjy
 * @version 1.0
 * @since 2025-08-13 12:36:40
 */
public class CacheStampedLock {

    private final StampedLock lock = new StampedLock();
    private volatile String data = "init";
    private volatile long version = 0;

    // 对外暴露的读取方法
    public String readData() {
        // 1. 乐观读：不加锁直接读
        long stamp = lock.tryOptimisticRead();
        String currentData = data;
        long currentVer;

        // 2. 验证：检查版本号是否被修改
        if (!lock.validate(stamp)) {
            // 3. 验证失败，升级为悲观读锁
            stamp = lock.readLock();
            try {
                currentData = data;
                currentVer = version;

                // 4. 发现数据过期，再升级为写锁刷新
                if (needRefresh(currentVer)) {
                    long ws = lock.tryConvertToWriteLock(stamp);
                    if (ws != 0L) {             // 升级成功
                        stamp = ws;             // 继续持写锁
                        data = loadFromDB();  // 重新加载
                        version++;
                        currentData = data;
                    } else {                    // 升级失败：释放读锁，再显式获取写锁
                        lock.unlockRead(stamp);
                        stamp = lock.writeLock();
                        try {
                            if (needRefresh(version)) { // 双重检查
                                data = loadFromDB();
                                version++;
                            }
                            currentData = data;
                        } finally {
                            lock.unlockWrite(stamp);
                        }
                        return currentData;
                    }
                }
            } finally {
                // 5. 释放悲观读锁或写锁
                if (lock.isReadLocked())
                    lock.unlockRead(stamp);
                else if (lock.isWriteLocked())
                    lock.unlockWrite(stamp);
            }
        }
        return currentData;
    }

    // 模拟判断是否需要刷新（例如超过5秒）
    private boolean needRefresh(long ver) {
        return System.currentTimeMillis() - ver > 5000;
    }

    // 模拟耗时加载
    private String loadFromDB() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException ignored) {
        }
        return "fresh-data@" + System.currentTimeMillis();
    }

}
